package com.qf.demo1.simple;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Consumer {
    public static void main(String[] args) throws IOException, TimeoutException {


        //创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //rabbitmq服务器ip(虚拟机写虚拟机ip)
        factory.setHost("localhost");
        //rabbitmq服务器端口
        factory.setPort(5672);
        //用户名&密码
        factory.setUsername("cdm");
        factory.setPassword("cdm");
        //设置虚拟注解
        factory.setVirtualHost("cdm");

        //获取连接
        Connection connection = factory.newConnection();
        //获取交互的channel通道
        Channel channel = connection.createChannel();

        //如果队列存在，不创建；如果不存在则创建
        channel.queueDeclare("simple.que",true,false,false,null);

        //消费消息
        /*
            queue – 消费的队列名称
            autoAck – 是否自动确认。true自动确认，消息会消费后，自动删除；false，需要手动确认后才会删除
            callback – 处理消息的回调
         */
        channel.basicConsume("simple.que",false,new DefaultConsumer(channel){
            //当消息到达后，触发的回调方法
            /*
                String consumerTag：消费端标记，每个消费端的标记唯一不重复的
                Envelope envelope:生产端信息的封装对象（生产端的唯一标识、交换机、路由键信息）
                AMQP.BasicProperties properties: 生产端发送消息时的额外数据
                byte[] body： 消息体
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                try {
                    System.out.println("consumerTag：" + consumerTag);
                    System.out.println("msg: " + new String(body, "utf-8"));

                    int i = 1/0;

                    /*
                    deliveryTag –  生产端唯一标识
                    multiple – 是否将之前未确认的所有消息全部确认。false-之确认当前消费到的这条
                     */
                    channel.basicAck(envelope.getDeliveryTag(), true);
                }catch (Exception e){
                    /*
                        deliveryTag –  生产端唯一标识
                        multiple – 是否将之前未确认的所有消息全部确认。false-之确认当前消费到的这条
                        requeue - 是否重新入队。true-重新入队重新消费；false-不入队（如果当前队列设置了死信队列，进入死信队列中，否则丢弃）
                     */
                    /**
                     * 当消费失败后，入队重新消费，保障消息可靠性，但是不可以无限入队，① 消费端cpu压力比较大 ② 可能导致消息中间件的消息积压
                     * 解决办法？？
                     *      失败了重试2-3次；如果2-3次后依然是失败的，要将消息持久化保存到数据库，并且给这条消息设置一个消费的标记（0 未消费 1 消费中 2 已消费）
                     *          可以开启一个定时任务，扫描这种消息表，获取其中消息状态为0的进行重新消费
                     *      重试次数的计数操作，需要借助redis中的incr来保障并发安全
                     */


                    channel.basicNack(envelope.getDeliveryTag(), true, false);

                }
            }
        });

//        channel.close();
//        connection.close();
    }
}
